package cn.agile4j.rpc;

import cn.agile4j.rpc.pojo.RpcRequest;
import cn.agile4j.rpc.pojo.RpcResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

/**
 * 处理 RPC 请求
 * 需扩展 Netty 的SimpleChannelInboundHandler抽象类
 *
 * Created by Administrator on 2016/8/16.
 */
public class RpcHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private static final Logger LOGGER = LoggerFactory.getLogger(RpcHandler.class);

    private final Map<String, Object> handlerMap ;

    public RpcHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception {
        RpcResponse response = new RpcResponse() ;
        response.setRequestId(request.getRequestId());
        try {
            Object result = handle(request);
            response.setResult(result);
        } catch (Throwable t) {
            response.setError(t);
        }
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE) ;
    }

    private Object handle(RpcRequest request) throws Throwable{
        String className = request.getClassName() ;
        Object serviceBean = handlerMap.get(className);

        Class<?> serviceClass = serviceBean.getClass() ;
        String methodName =  request.getMethodName() ;
        Class<?>[] parameterTypes = request.getParameterType() ;
        Object[] parameters = request.getPrameters() ;

/*
        Method method = serviceClass.getMethod(methodName, parameterTypes);
        method.setAccessible(true);
        return method.invoke(serviceBean, parameters);
*/

        // 基于CGLIB的反射
        FastClass serviceFastClass = FastClass.create(serviceClass) ;
        FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes) ;
        return serviceFastMethod.invoke(serviceBean, parameters) ;
    }

    /**
     * exceptionCaught事件处理方法由一个ExceptionEvent异常事件调用，
     * 这个异常事件起因于Netty的I/O异常或一个处理器实现的内部异常。多数情况下，
     * 捕捉到的异常应当被记录下来，并在这个方法中关闭这个channel通道。
     * 当然处理这种异常情况的方法实现可能因你的实际需求而有所不同，
     * 例如，在关闭这个连接之前你可能会发送一个包含了错误码的响应消息。
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("server caught exception", cause);
        ctx.close();
    }
}
